/******************************************************************************
 * Copyright 2022 The AIR Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#pragma once

#include <sys/types.h>

#include <iostream>
#include <memory>
#include <string>

#include <glog/logging.h>

#include "BIT_STRING.h"
#include "constr_TYPE.h"

template <asn_TYPE_descriptor_t const *descriptor, typename T>  //
struct asn1_operator_base {
  static T *create_tp(T **pp = nullptr) {
    if (nullptr != pp && nullptr != *pp) {
      return *pp;
    }
    auto *p = reinterpret_cast<T *>(calloc(sizeof(T), 1));
    if (!p) {
      LOG(ERROR) << "[HELPER ASN] Failed to create buffer.";
      return nullptr;
    }
    if (nullptr != pp) {
      *pp = p;
    }
    return p;
  }
  static std::shared_ptr<T> create_ptr() {
    auto p = create_tp();
    if (!p) {
      return nullptr;
    }
    return std::shared_ptr<T>(p, [](T *p) { ASN_STRUCT_FREE(*descriptor, p); });
  }
  static T *decode_tp(enum asn_transfer_syntax syntax, const void *buf,
                      size_t buflen) {
    void *p = nullptr;
    asn_dec_rval_t ret =
        asn_decode(nullptr, syntax, descriptor, &p, buf, buflen);
    if (RC_OK != ret.code) {
      LOG(ERROR) << "[HELPER ASN] Failed to decode!";
      ASN_STRUCT_FREE(*descriptor, p);
      return nullptr;
    }
    return reinterpret_cast<T *>(p);
  }
  static std::shared_ptr<T> decode_ptr(enum asn_transfer_syntax syntax,
                                       const void *buf, size_t buflen) {
    auto p = decode_tp(syntax, buf, buflen);
    if (!p) {
      return nullptr;
    }
    return std::shared_ptr<T>(p, [](T *p) { ASN_STRUCT_FREE(*descriptor, p); });
  }
  static ssize_t encode_tp(enum asn_transfer_syntax syntax, const T *st,
                           void **pp) {
    if (!pp) {
      LOG(ERROR) << "[HELPER ASN] Invalid params!";
      return -1;
    }
    auto encode_res = asn_encode_to_new_buffer(nullptr, syntax, descriptor, st);
    if (encode_res.result.encoded < 0) {
      LOG(ERROR) << "[HELPER ASN] Failed to encode: " << descriptor->name
                 << ". Failed type: " << encode_res.result.failed_type->name;
      if (encode_res.buffer) {
        free(encode_res.buffer);
      }
      return encode_res.result.encoded;
    }
    *pp = encode_res.buffer;
    return encode_res.result.encoded;
  }
  static std::string encode_str(enum asn_transfer_syntax syntax, const T *st) {
    void *p = nullptr;
    auto res_len = encode_tp(syntax, st, &p);
    if (res_len <= 0) {
      return "";
    }
    std::string result((const char *)p, res_len);
    free(p);
    return result;
  }
};

template <asn_TYPE_descriptor_t const *descriptor, typename T>
struct asn1_operator : public asn1_operator_base<descriptor, T> {};

#define ASN1(type) asn1_operator<&asn_DEF_##type, type##_t>
